#ifndef __ZWIDGET
#define __ZWIDGET

#include <iostream>
#include <string>
#include <vector>

#include <conio.h>
#include <windows.h>

using namespace std;

class Cloneable {
public:
	virtual Cloneable * clone(void) const = 0;
};

class Containable : public Cloneable {
protected:
	int x;
	int y;
	Containable(int x_, int y_) : x(x_), y(y_) {}
public:
	virtual int getX(void) const { return this->x; }
	virtual int getY(void) const { return this->y; }
	virtual void setX(int x_) { this->x = x_; }
	virtual void setY(int y_) { this->y = y_; }
};

class Container {
protected:
	vector<Cloneable*> list;
	const Cloneable * focus;
public:
	Container():focus(NULL) {}
	Cloneable * add(const Cloneable & d) {
		Cloneable * result = d.clone();
		list.push_back(result);
		return result;
	}
	void setFocus(const Cloneable * in) {
		focus = in;
	}
};

class Drawable : public Containable {
protected:
	Drawable(int x_, int y_) : Containable(x_, y_) {}
public:
	virtual void draw(const Containable *, bool isFocus) const = 0;
};

class Window : public Drawable, public Container {
private:
	int w_;
	int h_;
	int fstate;
	int bstate;
public:
	Window(int x_, int y_, int w__, int h__, int fstate_, int bstate_)
		:Drawable(x_, y_), w_(w__), h_(h__), fstate(fstate_), bstate(bstate_) {}
	virtual void draw(const Containable * containable, bool isFocus) const;
	virtual Window * clone(void) const { return new Window(*this); }	
};

class Text : public Drawable {
private:
	int w;
	int h;
	int bstate;
	int fstate;
	string text;
public:
	Text(int x_, int y_, int w_, int h_, int fstate_, int bstate_, string text_ = "") 
		: Drawable(x_, y_), w(w_), h(h_), fstate(fstate_), bstate(bstate_), text(text_) {}
	virtual Text * clone(void) const { return new Text(*this); }
	virtual void draw(const Containable * containable, bool isFocus_) const;
};
/// Display is singleton class
class Display : public Containable, public Container {
private:
	int screenWidth;
	int screenHeight;
	COORD dwBufferSize;
	COORD dwBufferCoord;
	SMALL_RECT rcRegion;
	CHAR_INFO * buffer;
	Display(int w, int h);

public:
	CHAR_INFO * at(int y, int x) {
		return buffer + screenWidth*y + x;
	}
	virtual int getX(void) const { return 0; }
	virtual int getY(void) const { return 0; }
	virtual void setX(int x) { throw "cannot set coordinate of display"; }
	virtual void setY(int y) { throw "cannot set coordinate of display"; }
	virtual Cloneable * clone(void) const { throw "cannot instantiate"; }

private:
	static int dummy;
	static int init(void);
	static Display * display;
public:
	void drawBox(int x, int y, int w, int h, int state);
	void drawString(int x, int y, int w, int h, const string & text);
	void render();
	void flush();
	void setText(string text) {
		SetConsoleTitle(text.data());
	}

	WORD getVirtualKey(void);

	static Display * getDisplay() { return Display::display; }
	static Display * createDisplay(int w, int h) {
		if (Display::display != NULL)
			delete Display::display;
		return Display::display = new Display(w, h);
	}

};

class Toolkit {
private:
	static const int FOREGROUND_WINDOW_STATE;
	static const int BACKGROUND_WINDOW_STATE;
	static const int FOREGROUND_TEXT_STATE;
	static const int BACKGROUND_TEXT_STATE;

public:
	static Window createWindow(int x_, int y_, int w_, int h_) {
		return Window(x_,y_,w_,h_, FOREGROUND_WINDOW_STATE, BACKGROUND_WINDOW_STATE);
	}

	static Text creatText(int x_, int y_, int w_, int h_, string text = "") {
		return Text( x_,y_,w_, h_,FOREGROUND_TEXT_STATE,BACKGROUND_TEXT_STATE, text);
	}
};
#endif